#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <getopt.h>

#include "stack.h"
#include <malloc.h>
#include <dirent.h>
#include <sys/stat.h>
#include <stdbool.h>
#include <stdlib.h>

#define MAX_LINE 500
#define MAX_FILENAME_LENGTH 20

typedef struct Params_Tag{
    int all;
    int ignore_blank;
    int recursive;
    int suffix;
    int help;
    char suffix_string[20];
    int is_success;
} Params;

typedef struct Line_cnt{
    int blank_lines;
    int nblank_lines;
    char* filename;
} linecnt;

const char *short_optstring="Abrhs:";

struct FileInfo{
    char *file_pathname;
}fileInfo;

static struct option long_Opts[]={
    {"all", no_argument, NULL, 'A'},
    {"suffix", required_argument, NULL, 's'},
    {"help", no_argument, NULL, 'h'},
    {NULL,0,NULL,0}
};

void copy_string(char* p1, char* p2) {
    //指针运算符比++优先级高
    //也就是先将*p1的值给*p2，再进行++操作，i++是先赋值，后自增
    while ((*p2++ = *p1++) != '\0'){}
}

linecnt cnt_line(char * filename){
    int nblank_lines = 0, blank_lines = 0;
    FILE* fp;
    if((fp=fopen(filename, "r")) == NULL){
        printf("can't open %s", filename);
        exit(1);
    }
    char buf[MAX_LINE];
    while(!feof(fp)){
        if(!fgets(buf, MAX_LINE, fp)){
            blank_lines++;
            continue;
        }
//        printf("%s", buf);
        if(!strcmp(buf, "\n"))
            blank_lines++;
        else
            nblank_lines++;
    }
//    printf("blank: %d, nblank: %d\n", blank_lines, nblank_lines);
    linecnt result = {blank_lines, nblank_lines, filename};
//    copy_string(filename, result.filename);
    fclose(fp);
    return result;
}
    
void parseParams(Params* param, int argc, char * argv[]){
    param->all = 0;
    param->recursive = 0;
    param->ignore_blank = 0;
    param->help = 0;
    param->suffix = 0;
    param->is_success = 1;

    int options_index = 0;
    int opt;
    while((opt = getopt_long(argc, argv, short_optstring,long_Opts, &options_index)) !=EOF){
        switch (opt)
        {
        case 'A':
        //    printf("A is detected\n");
            param->all = 1;
            break;
        case 'b':
            param->ignore_blank = 1;
            break;
        case 'r':
            param->recursive = 1;
            break;
        case 'h':
            param->help = 1;
            break;
        case 's':
            param->suffix = 1;
           // printf("suffix is:%s\n",optarg);
            strcpy(param->suffix_string, optarg);
            break;
        case '?':
            printf("?\n");
            param->is_success = -1;
            break;
        default:
            printf("unknown opt:%c", (char)opt);
        }
    }    
}

int is_matchedSuffix(char *pathName,  Params* param ){
    char *suffix = strrchr(pathName, '.');
    if(suffix !=NULL){
        if(strcmp(suffix+1, param->suffix_string) == 0){
            printf("the same");
            return 1;
        }
    }
    return 0;
}


stack* getAllFilesName( Params* param, int argc, char * argv[]){
    stack *unSearchStack;
    stack *fileStack;
    unSearchStack = (stack*) malloc(sizeof(stack));
    fileStack = (stack*) malloc(sizeof(stack));
    initialize(unSearchStack, sizeof(fileInfo));
    initialize(fileStack,sizeof(fileInfo));

    printf("tag1\n");

    while(optind < argc){
        char *path = malloc(strlen(argv[optind]));
        strcpy(path, argv[optind]);
        push(unSearchStack, &path);
        optind ++;
    }
    // printf("tag2\n");

    char *pathName;
    struct stat statbuf;
    while(!isEmpty(unSearchStack)){
        pathName = *(char**) pop(unSearchStack);
        // printf("now pathName:%s\n",pathName);
        if(lstat(pathName,&statbuf) < 0){
            printf("can't lstat this\n");
            free(pathName);
            continue;
        }
        // printf("tag2.2\n");
        if(S_ISREG(statbuf.st_mode)){
            // printf("tag2.2.1\n");
            if(  !param->suffix || param->suffix && is_matchedSuffix(pathName, param))
                push(fileStack, &pathName);
            else
                free(pathName);
        }
        else if(S_ISDIR(statbuf.st_mode ) ){
            // printf("tag2.2.2\n");
            DIR *dp;
            struct dirent *dirp;
            if((dp = opendir(pathName))==NULL){
                // printf("tag2.3.1\n");
                free(pathName);
                continue;
            }
            while((dirp = readdir(dp)) !=NULL){
                //ignore . and .. in dir
                if(strcmp(dirp->d_name,".")==0 || strcmp(dirp->d_name,"..") == 0)
                   continue;
                
                //get new pathName to record
                char * new_pathName = malloc(strlen(pathName) + strlen(dirp->d_name) + 1);
                strcpy(new_pathName, pathName);
                new_pathName[strlen(pathName)] = '/';
                strcpy(&new_pathName[strlen(pathName)+1], dirp->d_name);
                struct stat newFile_buff;
                // printf("tag2.3.2\n");
                // printf("a new PathName is:%s\n",new_pathName);

                //determine whether to save the newFile(DIR) to stack
                if(lstat(new_pathName,&newFile_buff) < 0)
                    free(new_pathName);
                else{
                    if(S_ISDIR(newFile_buff.st_mode) &&param->recursive){
                        // printf("tag2.3.3: push in a new dir \n");
                        push(unSearchStack, &new_pathName);
                    }
                    else if(S_ISREG(newFile_buff.st_mode) &&( !param->suffix || param->suffix&& is_matchedSuffix(new_pathName,param))){
                        // printf("tag2.3.3: push in a new file \n");
                        push(fileStack, &new_pathName);      
                    }
                    else
                        free(new_pathName);
                }
            }
            free(pathName);
        }
        else{
            // printf("tag2.3\n");
            free(pathName);
        }     
    }// finish unSearchStack
    return fileStack;
}

void format_print(linecnt *lines_list, int length, Params param){
    printf("line\tfile\n");
    printf("----\t----\n");
    int blank_cnt = 0, nblank_cnt = 0, total = 0;
    if (length == 1){
        const char *filename = lines_list[0].filename;
        blank_cnt = lines_list[0].blank_lines;
        nblank_cnt = lines_list[0].nblank_lines;
        if (param.ignore_blank)
            printf("%4d\t%s\n", nblank_cnt, filename);
        else
            printf("%4d\t%s\n", nblank_cnt+blank_cnt, filename);
    }
    for (int i = 0; i < length; ++i) {
        blank_cnt = lines_list[i].blank_lines;
        nblank_cnt = lines_list[i].nblank_lines;
        int total_ = blank_cnt + nblank_cnt;
        const char *filename = lines_list[i].filename;
        if (param.all){
            total += total_;
            continue;
        } else if (param.ignore_blank) {
            printf("%4d\t%s\n", nblank_cnt, filename);
            total += nblank_cnt;
            continue;
        } else {
            printf("%4d\t%s\n", total_, filename);
            total += total_;
            continue;
        }
    }
    printf("%4d\ttotal\n", total);
}


int main(int argc, char * argv[]){
    Params param;
    parseParams(&param, argc, argv);
    // printf("all: %i \n",param.all);
    // printf("count blank: %i \n",param.count_blank);
    // printf("recursive: %i \n",param.recursive);
    // printf("suffix: %i \n",param.suffix);
    // printf("suffix_string: %s \n",param.suffix_string);
    // printf("sc:%i",param.is_success);

    char *pathName = "hbc.c";
    // if(is_matchedSuffix(pathName, &param))
    //     printf("\n %s is match with suffix: %s\n",pathName, param.suffix_string);
    //
    stack *allFiles;

    allFiles = getAllFilesName( &param, argc, argv);
    int fileNum = allFiles->top;
    int tmp = fileNum;
    linecnt * fileLines = malloc(sizeof(linecnt) * fileNum);
//    printf("%d\n", allFiles->top);
    while(!isEmpty(allFiles)){
        pathName = *(char**) pop(allFiles);
//        printf("***file: %s\n",pathName);
        fileLines[fileNum-1] = cnt_line(pathName);
        fileNum--;
    }
//    for(int i = 0; i < tmp; i++){
//        printf("blank: %d, nblank: %d\n", fileLines[i].blank_lines, fileLines[i].nblank_lines);
//    }
    format_print(fileLines, tmp, param);
    free(fileLines);
    return 0;
}

